package com.yan.club.sys.service.impl;

import com.yan.club.exception.ServiceException;
import com.yan.club.result.Result;
import com.yan.club.sys.dto.SysRoleDto;
import com.yan.club.sys.mapper.SysRoleDeptMapper;
import com.yan.club.sys.mapper.SysRoleMapper;
import com.yan.club.sys.mapper.SysRoleMenuMapper;
import com.yan.club.sys.mapper.SysUserRoleMapper;
import com.yan.club.sys.model.*;
import com.yan.club.sys.service.SysRoleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.yan.club.auth.utils.SecurityUtil.getUserId;
import static com.yan.club.auth.utils.SecurityUtil.isAdmin;

/**
 * @author 彦
 * @since 2024/1/26 15:28
 */
@Service
public class SysRoleServiceImpl implements SysRoleService {

    @Resource
    private SysRoleMapper sysRoleMapper;

    @Resource
    private SysRoleMapper SysRoleMapper;

    @Resource
    private SysRoleMenuMapper SysRoleMenuMapper;

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    @Resource
    private SysRoleDeptMapper sysRoleDeptMapper;

    /**
     * 查询所有角色
     *
     * @param role
     * @return
     */
    @Override
    public List<SysRole> selectRoleList(SysRoleDto role) {
        return sysRoleMapper.selectRoleList(role);
    }

    /**
     * 判断角色名字是否存在
     *
     * @param role
     * @return
     */
    @Override
    public boolean checkRoleNameUnique(SysRole role) {
        long roleId = role.getRoleId() == null ? -1L : role.getRoleId();

        SysRoleExample ex = new SysRoleExample();
        ex.createCriteria().andRoleNameEqualTo(role.getRoleName())
                .andDelFlagEqualTo("0");
        List<SysRole> list = SysRoleMapper.selectByExample(ex);

        return !CollectionUtils.isEmpty(list) && list.get(0).getRoleId().equals(roleId);
    }

    /**
     * 判断角色权限是否存在
     *
     * @param role
     * @return
     */
    @Override
    public boolean checkRoleKeyUnique(SysRole role) {
        long roleId = role.getRoleId() == null ? -1L : role.getRoleId();

        SysRoleExample ex = new SysRoleExample();
        ex.createCriteria().andRoleKeyEqualTo(role.getRoleKey())
                .andDelFlagEqualTo("0");
        List<SysRole> list = SysRoleMapper.selectByExample(ex);

        return !CollectionUtils.isEmpty(list) && list.get(0).getRoleId().equals(roleId);
    }

    /**
     * 插入
     *
     * @param role
     */
    @Override
    public Result<String> insertRole(SysRoleDto role) {
        sysRoleMapper.insertRole(role);
        insertRoleMenu(role);
        return Result.success();
    }

    /**
     * 新增角色菜单信息
     * 把权限批量添加到角色
     *
     * @param role 角色对象
     */
    public void insertRoleMenu(SysRoleDto role) {

        // 新增用户与角色管理
        List<SysRoleMenu> list = new ArrayList<>();
        for (Long menuId : role.getMenuIds()) {
            SysRoleMenu rm = new SysRoleMenu();
            rm.setRoleId(role.getRoleId());
            rm.setMenuId(menuId);
            list.add(rm);
        }
        if (!list.isEmpty()) {
            SysRoleMenuMapper.batchInsert(list);
        }
    }

    /**
     * 删除角色
     *
     * @param roleIds
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRoleByIds(Long[] roleIds) {
        for (Long roleId : roleIds) {
            // 判断是否超级管理员
            checkRoleAllowed(new SysRole(roleId));
            // 判断是否有权限
            checkRoleDataScope(roleId);
            SysRole role = selectRoleById(roleId);
            // 判断是否被分配
            if (countUserRoleByRoleId(roleId) > 0) {
                throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName()));
            }
        }
//         删除角色与菜单关联
        SysRoleMenuExample ex = new SysRoleMenuExample();
        ex.createCriteria().andRoleIdIn(Arrays.asList(roleIds));
        SysRoleMenuMapper.deleteByExample(ex);

        // 删除角色
        SysRoleExample example = new SysRoleExample();
        example.createCriteria().andRoleIdIn(Arrays.asList(roleIds));
        SysRoleMapper.deleteByExample(example);
    }

    /**
     * 校验角色是否允许操作
     *
     * @param role 角色信息
     */
    @Override
    public void checkRoleAllowed(SysRole role) {
        if (role.getRoleId() != null && ("admin".equals(role.getRoleKey()) || 1 == role.getRoleId())) {
            throw new ServiceException("不允许操作超级管理员角色");
        }
    }

    /**
     * 校验角色是否有数据权限
     *
     * @param roleId 角色id
     */
    @Override
    public void checkRoleDataScope(Long roleId) {
        if (!isAdmin(getUserId())) {
            SysRoleDto role = new SysRoleDto();
            role.setRoleId(roleId);

            List<SysRole> roles = selectRoleList(role);
            if (StringUtils.isEmpty(roles)) {
                throw new ServiceException("没有权限访问角色数据！");
            }
        }
    }

    /**
     * 通过角色ID查询角色
     *
     * @param roleId 角色ID
     * @return 角色对象信息
     */
    @Override
    public SysRole selectRoleById(Long roleId) {
        SysRoleExample ex = new SysRoleExample();
        ex.createCriteria().andRoleIdEqualTo(roleId);
        List<SysRole> list = SysRoleMapper.selectByExample(ex);

        return list.get(0);
    }

    /**
     * 通过角色ID查询角色使用数量
     *
     * @param roleId 角色ID
     * @return 结果
     */
    public int countUserRoleByRoleId(Long roleId) {
        SysUserRoleExample example = new SysUserRoleExample();
        example.createCriteria().andRoleIdEqualTo(roleId);

        return (int) sysUserRoleMapper.countByExample(example);
    }

    @Override
    public Result<String> updateRoleStatus(SysRole role) {

        sysRoleMapper.updateRole(role);
        return Result.success();
    }

    @Override
    public Result<String> updateRole(SysRoleDto role) {

        // 修改角色信息
        sysRoleMapper.updateRole(role);

        // 删除角色与菜单关联
        SysRoleMenuExample example = new SysRoleMenuExample();
        example.createCriteria().andRoleIdEqualTo(role.getRoleId());
        SysRoleMenuMapper.deleteByExample(example);
        insertRoleMenu(role);
        return Result.success();
    }

    /**
     * 修改数据权限信息
     *
     * @param role 角色信息
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int authDataScope(SysRole role) {

        // 修改角色信息
        sysRoleMapper.updateRole(role);
        // 删除角色与部门关联
        SysRoleDeptExample ex = new SysRoleDeptExample();
        ex.createCriteria().andRoleIdEqualTo(role.getRoleId());
        sysRoleDeptMapper.deleteByExample(ex);
        // 新增角色和部门信息（数据权限）
        return insertRoleDept(role);
    }

    /**
     * 新增角色部门信息(数据权限)
     *
     * @param role 角色对象
     */
    public int insertRoleDept(SysRole role) {
        int rows = 1;
        // 新增角色与部门（数据权限）管理
        List<SysRoleDept> list = new ArrayList<>();
        for (Long deptId : role.getDeptIds()) {
            SysRoleDept rd = new SysRoleDept();
            rd.setRoleId(role.getRoleId());
            rd.setDeptId(deptId);
            list.add(rd);
        }
        if (list.size() > 0) {
            rows = sysRoleDeptMapper.batchRoleDept(list);
        }
        return rows;
    }

    /**
     * 根据用户ID查询角色
     *
     * @param userId 用户ID
     * @return 角色列表
     */
    @Override
    public List<SysRole> selectRolesByUserId(Long userId) {
        List<SysRole> userRoles = sysRoleMapper.selectRolePermissionByUserId(userId);
        List<SysRole> roles = selectRoleList(new SysRoleDto());
        for (SysRole role : roles) {
            for (SysRole userRole : userRoles) {
                if (role.getRoleId().equals(userRole.getRoleId())) {
                    role.setFlag(true);
                    break;
                }
            }
        }
        return roles;
    }

    /**
     * 取消授权用户角色
     *
     * @param userRole 用户和角色关联信息
     * @return 结果
     */
    @Override
    public Result<String> deleteAuthUser(SysUserRole userRole) {
        SysUserRoleExample ex = new SysUserRoleExample();
        ex.createCriteria().andUserIdEqualTo(userRole.getUserId())
                .andRoleIdEqualTo(userRole.getRoleId());
        sysUserRoleMapper.deleteByExample(ex);
        return Result.success();
    }

    /**
     * 批量选择授权用户角色
     *
     * @param roleId  角色ID
     * @param userIds 需要授权的用户数据ID
     * @return 结果
     */
    @Override
    public int insertAuthUsers(Long roleId, Long[] userIds) {
        // 新增用户与角色管理
        List<SysUserRole> list = new ArrayList<>();
        for (Long userId : userIds) {
            SysUserRole ur = new SysUserRole();
            ur.setUserId(userId);
            ur.setRoleId(roleId);
            list.add(ur);
        }
        return sysUserRoleMapper.batchUserRole(list);
    }

    @Override
    public void importTemplate(HttpServletResponse response) {
        try {
            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("用户导入模版.xlsx");
            if (resourceAsStream == null) {
                throw new ServiceException("模板不存在，请联系管理员！");
            }
            // 获取要下载的模板名称
            String fileName = "用户导入模版.xlsx";
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            // 下载文件能正常显示中文
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setHeader("fileName", fileName);
            response.setHeader("Access-Control-Expose-Headers", "filename");

            OutputStream out = response.getOutputStream();
            byte[] b = new byte[2048];
            int len;
            while ((len = resourceAsStream.read(b)) != -1) {
                out.write(b, 0, len);
            }
            out.close();
            resourceAsStream.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
